﻿using System.Collections.Generic;
using Microsoft.Xna.Framework.Audio;
using ShevaEngine.Core.Interfaces;
using System;

namespace ShevaEngine.Core.Modules.Audio
{
    /// <summary>
    /// Audio system.
    /// </summary>
    public class AudioSystem : IUpdateAble
    {
        /// <summary>Singleton.</summary>
        public static readonly AudioSystem Instance = new AudioSystem();

        /// <summary>Listener.</summary>
        private List<SoundListener> SoundListeners;
        /// <summary>Audio listeners.</summary>
        private AudioListener[] AudioListeners;
        /// <summary>Temp emitter.</summary>
        private AudioEmitter TempEmitter = new AudioEmitter();
        /// <summary>Sounds.</summary>
        private List<Sound> Sounds;

        /// <summary>Sound effects.</summary>
        private Dictionary<string, SoundEffect> SoundEffects;

        /// <summary>
        /// Constructor.
        /// </summary>
        private AudioSystem()
        {
            this.SoundEffects = new Dictionary<string, SoundEffect>();
            this.Sounds = new List<Sound>();

            // Set the scale for 3D audio so it matches the scale of our game world.
            // DistanceScale controls how much sounds change volume as you move further away.
            // DopplerScale controls how much sounds change pitch as you move past them.
            SoundEffect.DistanceScale = 50;
            SoundEffect.DopplerScale = 0.1f;

            this.SoundListeners = new List<SoundListener>();
            this.AudioListeners = new AudioListener[0];
        }

        /// <summary>
        /// Method returns sound effect for name.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public SoundEffect GetSoundEffect(string name)
        {
            if (!this.SoundEffects.ContainsKey(name))
                this.SoundEffects.Add(name, ShevaEngine.Instance.Content.Load<SoundEffect>("Sounds\\" + name));

            return this.SoundEffects[name];
        }

        /// <summary>
        /// Method Update().
        /// </summary>
        /// <param name="time"></param>
        public void Update(Microsoft.Xna.Framework.GameTime time)
        {
            for (int i = this.SoundListeners.Count - 1; i >= 0; i--)
                this.SoundListeners[i].Update(time);

            for (int i = this.Sounds.Count - 1; i >= 0; i--)
            {
                this.Sounds[i].Update(time);
                this.Sounds[i].Apply3D(this.AudioListeners);
            }
        }

        /// <summary>
        /// Method adds sound.
        /// </summary>
        /// <param name="sound"></param>
        public void AddSound(Sound sound)
        {
            this.Sounds.Add(sound);

            if (sound.Is3D)
                sound.Apply3D(this.AudioListeners);
        }

        /// <summary>
        /// Method removes sound.
        /// </summary>
        /// <param name="sound"></param>
        public void RemoveSound(Sound sound)
        {
            if (this.Sounds.Contains(sound))
                this.Sounds.Remove(sound);
        }

        /// <summary>
        /// Method adds sound listener.
        /// </summary>
        /// <param name="soundListener"></param>
        public void AddSoundListener(SoundListener soundListener)
        {
            this.SoundListeners.Add(soundListener);

            this.AudioListeners = new AudioListener[this.SoundListeners.Count];

            for (int i = 0; i < this.SoundListeners.Count; i++)
                this.AudioListeners[i] = this.SoundListeners[i].Listener;
        }

        /// <summary>
        /// Method adds sound listener.
        /// </summary>
        /// <param name="soundListener"></param>
        public void RemoveSoundListener(SoundListener soundListener)
        {
            if (this.SoundListeners.Contains(soundListener))
                this.SoundListeners.Remove(soundListener);

            this.AudioListeners = new AudioListener[this.SoundListeners.Count];

            for (int i = 0; i < this.SoundListeners.Count; i++)
                this.AudioListeners[i] = this.SoundListeners[i].Listener;
        }
    }
}
